//
// Created by q on 2019/7/3.
//
#include "com_example_ndktest_JNIHelper.h"
#include "string.h"
#ifdef __cplus
#include <iostream>
#endif
#ifdef __cplus
#include "algorithm"
#endif
#include <android/log.h>
#include <algorithm>
#include "vector"

using namespace std;
// Android log function wrappers
static const char* kTAG = "testNDK";
#define LOGI(...) \
    ((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
#define LOGE(...) \
    ((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__))

struct GLOBAL_CONTEXT {
    JavaVM* javaVM;
    jclass mainActivityClz;
    jobject mainActivityObj;

    jclass jniHelperClz;
    jobject jniHelperObj;
};

/*
* 全局引用
*/
GLOBAL_CONTEXT mContext;

void sendJavaMsg(JNIEnv *env, jobject instance, jmethodID func, const char *msg) {
    jstring javaMsg = env -> NewStringUTF(msg);
    env -> CallVoidMethod(instance, func, javaMsg);
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {

    LOGI("JNI_OnLoad");

    JNIEnv* env;

    mContext.javaVM = vm;

    if (vm -> GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        LOGE("JNI_ERR");
        return JNI_ERR; // 不支持该版本
    }

    // 对JNIHelper设置全局引用
    jclass clz = env -> FindClass("com/example/ndktest/JNIHelper");
    mContext.jniHelperClz = (jclass) env -> NewGlobalRef(clz);

    jmethodID jniHelperCtor = env -> GetMethodID(mContext.jniHelperClz, "<init>", "()V");
    jobject handler = env -> NewObject(mContext.jniHelperClz, jniHelperCtor);
    mContext.jniHelperObj = env -> NewGlobalRef(handler);

    // 调用JNIHelper中updateStatus方法打印信息
    jmethodID statusId = env -> GetMethodID(mContext.jniHelperClz, "updateStatus", "(Ljava/lang/String;)V");
    sendJavaMsg(env, mContext.jniHelperObj, statusId, "JNI:initializing...");

    return JNI_VERSION_1_6;
}

/**
 * 简单返回一个字符串
 */
JNIEXPORT jstring JNICALL Java_com_example_ndktest_JNIHelper_getStringFromNative
  (JNIEnv *env, jclass jclz) {
    return env -> NewStringUTF("from Native");
}

/**
 * 根据排序好的vector集合，创建排序数组返回
 */
void callSetArrayResult(JNIEnv *env, vector<int> vector_) {

    int size = vector_.size();

    // 排序后需要返回的数组
    jintArray array = env -> NewIntArray(size);

    // 为jni数组赋值
    jint *num = new jint[size];
    for (int i = 0; i < size; i++) {
        num[i] = vector_[i];
    }
    env -> SetIntArrayRegion(array, 0, size, num); // 将num数据赋值到array中

    // 调用java方法传递排序好的数据
    jmethodID methodID = env -> GetMethodID(mContext.jniHelperClz, "callback", "([I)V");
    env -> CallVoidMethod(mContext.jniHelperObj, methodID, array);

    // 释放数组
    env -> ReleaseIntArrayElements(array, num, 0);
}

/**
 * 排序方法，成功返回true，否则false
 */
JNIEXPORT jboolean JNICALL Java_com_example_ndktest_JNIHelper_doSort
    (JNIEnv *env, jclass type, jintArray arr_) {

    // 获取传递过来的数组指针
    jint *nums = env -> GetIntArrayElements(arr_, NULL);
    // 获取数组长度
    jsize len = env -> GetArrayLength(arr_);

    // 使用标准库中的vector排序
    vector<int> mVector;
    for (int i = 0; i < len; i++) {
        mVector.push_back(nums[i]);
    }
    sort(mVector.begin(), mVector.end());

    // 调用返回结果函数
    callSetArrayResult(env, mVector);

    // 释放数组
    env -> ReleaseIntArrayElements(arr_, nums, 0);

    // 判断是否有异常，没有表示成功返回true，否则false
    jthrowable ex = env -> ExceptionOccurred();
    if (ex != 0) {
        env -> ExceptionClear();
        LOGE("Exception!");
        return false;
    } else {
        return true;
    }
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_ndktest_JNIHelper_accessField(JNIEnv *env, jobject instance) {
    jclass cls = env -> GetObjectClass(instance);
    jfieldID fid = env -> GetFieldID(cls, "s", "Ljava/lang/String;");
    jstring jstr = static_cast<jstring>(env -> GetObjectField(instance, fid));
    char *c_str = (char *) env -> GetStringUTFChars(jstr, JNI_FALSE);
    char text[20] = "super ";
    strcat(text, c_str);
    jstring new_jstr = env -> NewStringUTF(text);
    env -> SetObjectField(instance, fid, new_jstr);
    env -> ReleaseStringUTFChars(jstr, c_str);
    return new_jstr;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_ndktest_JNIHelper_accessStaticField(JNIEnv *env, jobject instance) {
    jclass cls = env -> GetObjectClass(instance);
    jfieldID fid = env -> GetStaticFieldID(cls, "count", "I");
    jint count = env -> GetStaticIntField(cls, fid);
    count++;
    env -> SetStaticIntField(cls, fid, count);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_ndktest_JNIHelper_accessStaticMethod(JNIEnv *env, jobject instance) {
    jclass cls = env -> GetObjectClass(instance);
    jmethodID mid = env -> GetStaticMethodID(cls, "getUUID", "()Ljava/lang/String;");
    jstring uuid = static_cast<jstring>(env -> CallStaticObjectMethod(cls, mid));
    return uuid;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_ndktest_JNIHelper_accessMethod(JNIEnv *env, jobject instance) {
    jclass cls = env -> GetObjectClass(instance);
    jmethodID mid = env -> GetMethodID(cls, "genRandomInt", "(I)I");
    jint random = env ->CallIntMethod(instance, mid, 200);
    return random;
}